﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;

namespace NNCore
{
    public class Matrix
    {
        [XmlIgnore]
        private readonly double[,] _matrix;

        public Matrix(int dim1, int dim2)
        {
            _matrix = new double[dim1, dim2];
        }

        public int Height { get { return _matrix.GetLength(0); } }
        public int Width { get { return _matrix.GetLength(1); } }

        public double this[int x, int y]
        {
            get { return _matrix[x, y]; }
            set { _matrix[x, y] = value; }
        }

        public double[] Vector
        {
            get
            {
                double [] serializedWeights = new double[(Width * Height)];
                for (int i = 0; i < Height; i++)
                    for (int j = 0; j < Width; j++)
                        serializedWeights[i * Width + j] = _matrix[i, j];
                return serializedWeights;
            }

            set
            {
                for (int i = 0; i < Height; i++)
                    for (int j = 0; j < Width; j++)
                        this[i, j] = value[i * Width + j];
            }
        }

        public unsafe static Matrix UnsafeMultiplication(Matrix m1, Matrix m2)
        {
            int h = m1.Height;
            int w = m2.Width;
            int l = m1.Width;
            Matrix resultMatrix = new Matrix(h, w);

            unsafe
            {
                fixed (double* pm = resultMatrix._matrix, pm1 = m1._matrix, pm2 = m2._matrix)
                {
                    int i1, i2;
                    for (int i = 0; i < h; i++)
                    {
                        i1 = i * l;
                        for (int j = 0; j < w; j++)
                        {
                            i2 = j;
                            double res = 0;
                            for (int k = 0; k < l; k++, i2 += w)
                            {
                                res += pm1[i1 + k] * pm2[i2];
                            }
                            pm[i * w + j] = res;
                        }
                    }
                }
            }
            return resultMatrix;
        }
        
        public static Matrix NaiveMultiplication(Matrix m1, Matrix m2)
        {
            Matrix resultMatrix = new Matrix(m1.Height, m2.Width);
            for (int i = 0; i < resultMatrix.Height; i++)
            {
                for (int j = 0; j < resultMatrix.Width; j++)
                {
                    resultMatrix[i, j] = 0;
                    for (int k = 0; k < m1.Width; k++)
                    {
                        resultMatrix[i, j] += m1[i, k] * m2[k, j];
                    }
                }
            }
            return resultMatrix;
        }

        public static Matrix createHorVector(double[] input)
        {
            Matrix vectorMatrix = new Matrix(1, input.Length);

            for (int i = 0; i < input.Length; i++)
            {
                vectorMatrix[0, i] = input[i];
            }

            return vectorMatrix;
        }

        public override string ToString()
        {
            string result = "";

            for (int i = 0; i < Height; i++)
            {
                for (int j = 0; j < Width; j++)
                {
                    result += this[i, j] + ", ";
                }
                result += "\n";
            }
            return result;
        }

    }
}
